package com.blue.crawler.engine;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.blue.core.dict.UploadType;
import com.blue.core.util.UrlUtil;
import com.blue.crawler.CrawlerException;
import com.blue.crawler.annotation.CrawlerConfig;
import com.blue.crawler.util.CrawlerInvoker;

public class ListDetailEngine<T extends ListDetailModel> implements Engine
{
	private static Logger logger = LoggerFactory.getLogger(ListDetailEngine.class);
	
	private final CrawlerInvoker invoker;
	private final ListDetailCrawl<T> crawl;
	private final Download download;

	public ListDetailEngine(CrawlerInvoker invoker, ListDetailCrawl<T> crawl, Download download)
	{
		this.invoker = invoker;
		this.crawl = crawl;
		this.download = download;
	}

	@Override
	public final void handle(String html, CrawlerConfig config, int page) throws Exception
	{
		List<T> list = crawl.queryList(html, config);
		if (list == null || list.isEmpty())
		{
			logger.warn("列表个数：空");
			return;
		}

		logger.info("列表个数：{}", list.size());
		if (config.isReverse()) // 反转
			Collections.reverse(list);
		
		for (T obj : list)
		{
			if (crawl.existUrl(obj.getUrl()))
			{
				if (config.isExistAndStop())
					throw new CrawlerException(config.getName() + " 有重复，停止爬虫");
					
				continue;
			}
			
			this.handleOne(obj, config);
			this.sleep(config);
		}
	}

	private void handleOne(T obj, CrawlerConfig config) throws IOException
	{
		String url = UrlUtil.getLink(config.getUrl(), obj.getUrl());
		if (obj.getCover() != null && !obj.getCover().isEmpty())
		{
			obj.setCover(UrlUtil.getLink(config.getUrl(), obj.getCover()));
		}
		
		Queue<String> pageQueue = new LinkedList<>();
		pageQueue.offer(url);
		Set<String> pageSet = new HashSet<>();
		pageSet.add(url);
		List<String> pageContent = new ArrayList<>();
		
		Set<String> urlSet = new LinkedHashSet<>(); // 附件URL
		
		
		int i = 1;
		while ((url = pageQueue.poll()) != null)
		{
			logger.info("解析：{}", url);
			this.sleep(config);
			String html = this.invoke(invoker, url, config);
			List<String> urlList = crawl.query(html, config, obj);
			if (urlList != null && !urlList.isEmpty())
			{
				for (String u : urlList)
				{
					urlSet.add(UrlUtil.getLink(url, u));
				}
			}
			
			if (obj.getContent() != null && !obj.getContent().isEmpty() && !pageContent.contains(obj.getContent()))
				pageContent.add(obj.getContent());
			
			List<String> pList = crawl.queryPage(html, config, obj, i);
			if (pList != null)
			{
				for (String p : pList)
				{
					p = UrlUtil.getLink(url, p);
					if (!pageSet.contains(p))
					{
						pageSet.add(p);
						pageQueue.offer(p);
					}
				}
			}
			i++;
			
		}
		String content = StringUtils.join(pageContent, "\n<!-- break_page -->\n");
		obj.setContent(content);
		
		// 下载图片
		this.download(obj, config, urlSet);
		
		crawl.handleModel(config, obj);
	}
	
	private void download(T obj, CrawlerConfig config, Set<String> urlSet)
	{
		if (!config.isDownload())
			return;
		
		// 下载封面
		if (obj.getCover() != null && !obj.getCover().isEmpty())
		{
			FileModel coverModel = download.download(obj.getCover(), obj.getCreateTime(), config);
			if (coverModel != null)
			{
				obj.setCoverFile(coverModel);
			}
		}
		
		// 附件
		List<FileModel> fileModelList = new ArrayList<>();
		String content = obj.getContent();
		for (String picUrl : urlSet)
		{
			FileModel model = download.download(picUrl, obj.getCreateTime(), config);
			if (model != null)
			{
				fileModelList.add(model);
				content = content.replaceAll(picUrl, model.getPath());
			}
		}
		
		// 封面不存在，附件第一张图作封面
		if (obj.getCoverFile() == null && fileModelList.size() > 0)
		{
			for (FileModel fileModel : fileModelList)
			{
				if (fileModel.getType() == UploadType.PIC)
				{
					obj.setCoverFile(fileModel);
					break;
				}
			}
		}
		
		obj.setFileList(fileModelList);
		obj.setContent(content);
	}
	
}
